package com.soboot.auth.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.soboot.auth.service.BaseUserDetailService;
import com.soboot.auth.translator.BaseResponseExceptionTranslator;
import com.soboot.base.constant.BaseConstant;
import com.soboot.base.model.BaseUser;
import com.soboot.common.config.redis.RedissonBaseService;
import com.soboot.handler.BaseAccessDeniedHandler;
import com.soboot.handler.BaseAuthenticationEntryPoint;
import com.soboot.security.base.RedisTokenStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author:
 * @createTime: 2022年09月20日 22:58:23
 * @version:
 * @Description:
 * @Copyright:
 */
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private BaseUserDetailService baseUserDetailService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedissonBaseService redissonBaseService;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private BaseResponseExceptionTranslator exceptionTranslator;

    @Autowired
    private BaseAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private BaseAccessDeniedHandler accessDeniedHandler;

    /**
     * 从数据库加载认证信息
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.withClientDetails(clientDetailsService());
    }

    /**
     * 用来配置令牌端点(Token Endpoint)的安全约束.
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);
    }

    /**
     * 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore())
                .tokenServices(tokenServices())
                .userDetailsService(baseUserDetailService)
                .authenticationManager(authenticationManager)
                .exceptionTranslator(exceptionTranslator);
    }

    @Bean
    public ClientDetailsService clientDetailsService() {
        //客户端配置，将客户端client id secret这些信息存储到数据库
        return new JdbcClientDetailsService(dataSource);
    }

    @Bean
    public TokenStore tokenStore(){
        //存储令牌
        return new RedisTokenStore(redisConnectionFactory);
    }
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setClientDetailsService(clientDetailsService());
        tokenServices.setTokenEnhancer(tokenEnhancer());
//        // token有效期自定义设置，默认12小时 设置为24小时86400
//        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 24 * 1);
//        // refresh_token默认30天 设置为7天604800
//        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        return tokenServices;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            final Map<String, Object> additionalInfo = new HashMap<>();
            BaseUser user = (BaseUser) authentication.getUserAuthentication().getPrincipal();
            additionalInfo.put(BaseConstant.UserAdditionalInfo.LICENSE.getKey(), BaseConstant.UserAdditionalInfo.LICENSE.getVal());
            additionalInfo.put(BaseConstant.UserAdditionalInfo.USERID.getKey(), user.getId());
            additionalInfo.put(BaseConstant.UserAdditionalInfo.USERNAME.getKey(), user.getUsername());
            additionalInfo.put(BaseConstant.UserAdditionalInfo.NICKNAME.getKey(), user.getNickName());

            if (StrUtil.isNotBlank(user.getDeptName())){
                additionalInfo.put(BaseConstant.UserAdditionalInfo.DEPT.getKey(), user.getDeptName());
            }
            if (CollUtil.isNotEmpty(user.getRoles())){
                additionalInfo.put(BaseConstant.UserAdditionalInfo.ROLE.getKey(), user.getRoles());
            }
            if (CollUtil.isNotEmpty(user.getPosts())){
                additionalInfo.put(BaseConstant.UserAdditionalInfo.POST.getKey(), user.getPosts());
            }
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            return accessToken;
        };
    }
}
